package com.linghang.back.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.linghang.back.dto.ApiResponse;
import com.linghang.back.exception.ErrorCode;
import com.linghang.back.util.JwtUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

@Component
public class JwtFilter extends OncePerRequestFilter {
    
    private static final Logger logger = LoggerFactory.getLogger(JwtFilter.class);
    
    @Autowired
    private JwtUtil jwtUtil;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    // 白名单路径，不需要验证token
    private static final List<String> WHITE_LIST = Arrays.asList(
            "/api/user/login",
            "/api/user/register",
            "/api/user/verify-code",
            "/api/articles/sync",
            "/api/articles",
            "/swagger-ui.html",
            "/swagger-ui/**", 
            "/v3/api-docs/**",
            "/swagger-resources/**",
            "/webjars/**",
            "/favicon.ico",
            "/error"
    );
    
    private final AntPathMatcher pathMatcher = new AntPathMatcher();
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        
        String path = request.getRequestURI();
        logger.debug("Filtering request: {} {}", request.getMethod(), path);
        
        // 处理预检请求
        if (HttpMethod.OPTIONS.matches(request.getMethod())) {
            logger.debug("Skipping JWT filter for OPTIONS request to: {}", path);
            filterChain.doFilter(request, response);
            return;
        }
        
        // 如果请求在白名单中，直接放行
        if (isWhiteListedPath(path)) {
            logger.debug("Path is whitelisted: {}", path);
            filterChain.doFilter(request, response);
            return;
        }
        
        try {
            // 从请求头中获取token
            String authHeader = request.getHeader("Authorization");
            
            // 如果没有token或token格式不对，返回401
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                logger.debug("Missing or invalid Authorization header for path: {}", path);
                handleUnauthorized(response, ErrorCode.UNAUTHORIZED);
                return;
            }
            
            String token = authHeader.substring(7);
            
            // 验证token
            String userId = jwtUtil.getUserIdFromToken(token);
            if (!jwtUtil.validateToken(token, userId)) {
                logger.debug("Token expired for user: {}", userId);
                handleUnauthorized(response, ErrorCode.TOKEN_EXPIRED);
                return;
            }
            
            // 将用户ID添加到请求属性中，便于后续处理
            request.setAttribute("userId", userId);
            
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            logger.error("JWT验证失败", e);
            handleUnauthorized(response, ErrorCode.TOKEN_INVALID);
        }
    }
    
    private boolean isWhiteListedPath(String path) {
        return WHITE_LIST.stream()
                .anyMatch(pattern -> pathMatcher.match(pattern, path));
    }
    
    private void handleUnauthorized(HttpServletResponse response, ErrorCode errorCode) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        
        ApiResponse<?> apiResponse = ApiResponse.fail(errorCode.getCode(), errorCode.getMessage());
        response.getWriter().write(objectMapper.writeValueAsString(apiResponse));
    }
} 